package fox.framework.rpc.endpoint.netty.client;

import fox.framework.rpc.client.message.response.ByteSourceResponseEntity;
import fox.framework.rpc.client.message.response.RpcClientResponse;
import fox.framework.rpc.client.message.response.RpcClientResponseBuilder;
import fox.framework.rpc.common.StringUtils;
import fox.framework.rpc.common.WebUtils;
import fox.framework.rpc.exception.RpcDecodeWithRequestIdException;
import fox.framework.rpc.exception.RpcRecordException;
import fox.framework.rpc.message.MappedRpcHeader;
import fox.framework.rpc.message.RpcHeader;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.ByteProcessor;
import io.netty.util.ReferenceCountUtil;

import java.util.List;

/**
 * @author cuichao
 * @Description: FoxClientResponseDecoder
 * @Date: create in 2021/1/24 21:50
 */
public class FoxClientResponseDecoder extends ByteToMessageDecoder {


    private static final String PROTOCOL_NAME = "fox";
    private static final int PROTOCOL_MIN_LEN = 2;
    private static final String PROTOCOL_SPILT = "\n";
    private static final String PROTOCOL_ENTRY_SPILT = " ";
    private static final String HEADER_ENTRY_SPILT = ";";
    private static final String HEADER_KV_SPILT = ":";

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        MappedRpcHeader header = new MappedRpcHeader();
        RpcClientResponseBuilder builder = new RpcClientResponseBuilder();
        /**
         *  decode protocol
         */
        int protocol_len = in.forEachByte(ByteProcessor.FIND_LF);
        //not match
        if(protocol_len <= 0){
            ctx.fireChannelRead(in);
            return;
        }
        byte[] protocolBuffer = new byte[protocol_len+1];
        in.readBytes(protocolBuffer);
        String protocolBlock  = new String(protocolBuffer,0 , protocolBuffer.length -1);
        //以空格为间隔
        String[] protocols = protocolBlock.split(PROTOCOL_ENTRY_SPILT);
        if(protocols.length < PROTOCOL_MIN_LEN || !PROTOCOL_NAME.equals(protocols[0])){
            in.resetReaderIndex();
            ReferenceCountUtil.retain(in);
            ctx.fireChannelRead(in);
            return;
        }
        String protocol = protocols[0];
        int status = Integer.valueOf(protocols[1]);
        builder.setProtocol(protocol);
        builder.setStatus(status);
        /**
         * parse header
         */
        int header_len = in.forEachByte(protocol_len+1, in.readableBytes(), ByteProcessor.FIND_LF) - protocol_len;
        if(header_len <= 0 ){
            String msg = "the response-header is empty";
            throw new RpcRecordException(msg);
        }
        byte[] headerBuffer = new byte[header_len];
        in.readBytes(headerBuffer);
        String headerBlock = new String(headerBuffer,0,headerBuffer.length -1 );
        String[] entries = headerBlock.split(HEADER_ENTRY_SPILT);
        for (String entry : entries) {
            String[] headerEntry = entry.split(HEADER_KV_SPILT);
            if(headerEntry.length == 2){
                header.setHeader(headerEntry[0],WebUtils.urlDecode(headerEntry[1]));
                builder.setHeader(headerEntry[0],WebUtils.urlDecode(headerEntry[1]));

            }
        }

        if(StringUtils.isEmpty(header.getHeader(RpcHeader.REQUEST_ID))){
            String msg = "the request-id is empty";
            throw new RpcRecordException(msg);
        }

        /**
         * valid header must entry
         */
        if(StringUtils.isEmpty(header.getHeader(RpcHeader.CONTENT_LENGTH))
                || StringUtils.isEmpty(header.getHeader(RpcHeader.CONTENT_TYPE))){
            String msg = "the response header miss content-length or content-type";
            throw new RpcDecodeWithRequestIdException(protocol,header.getHeader(RpcHeader.REQUEST_ID),msg);
        }
        /**
         * read byte body and build request
         */
        int contentLength = 0;
        try {
            contentLength = Integer.valueOf(header.getHeader(RpcHeader.CONTENT_LENGTH));
        }catch (NumberFormatException e){
            String msg = "the content-length not be cast Integer";
            throw new RpcDecodeWithRequestIdException(protocol,header.getHeader(RpcHeader.REQUEST_ID),msg);
        }
        byte[] body = new byte[contentLength];
        if(contentLength > 0){
            if(in.readableBytes() < contentLength){
                in.resetReaderIndex();
                return;
            }
            in.readBytes(body);
        }
        builder.setResponseEntity(new ByteSourceResponseEntity(body,status));
        RpcClientResponse response = builder.build();
        out.add(response);
    }
}
